/*
 * Rev_Dictator.java
 *
 * Defines the player 2 version of the Dictator game.
 */

package game;

import java.awt.Cursor;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.MemoryImageSource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.Timer;

/**
 * Defines the player 2 version of the Dictator game. Just asks the user how
 * much they think the other player gave them. Then randomly assigns how much
 * player 1 gave them based on the probability distribution specified in the
 * CSV file.
 * 
 * @author Justin
 */
public class Rev_Dictator extends Game {
    /** The amount of money ($) player 1 has. Also serves as the maximum amount
     of money player 2 can guess. */
    double endowment;
    
    /** The increment ($) player 1 had to use to give player 2 any money. Also
     serves as the increment player 2 can use to guess. */
    double increment;

    /** The amount player 2 guesses player 1 gave him. */
    double guess;

    /** The amount player 2 actually "gets" from player 1 (from data file). */
    double given;

    /** The amount of time, in seconds, available for the game. */
    final int GAME_TIME = 180;

    /** The think time of player 1. */
    int thinkTimeRemaining;

    /** The file that holds the questionnaire data in CSV format. */
    final String DATA_FILE = "Dictator-Player2_data.csv";

    /** Timer for the game countdown. */
    Timer timer = new Timer (1000, new TimeListener());

    /** Timer for player 1 think. */
    Timer thinkTimer = new Timer(1000, new ThinkTimeListener());

    /** Indicates what part of the game we're on. */
    int gameState;

    // Game state constants.
    final int GS_GUESS = 0;
    final int GS_WAITING = 1;
    final int GS_RESULT = 2;

    // CSV file constants.
    final int CSV_AMOUNTIND = 0;
    final int CSV_PROBIND = 1;

	// End game constants.
	final int EG_NORMAL = 0;
	final int EG_BADFILE = 1;
	final int EG_TIMEOUT = 2;

	/** List of values that user can choose to send. Used in chooser JList. */
	LinkedList<String> moneyValues = new LinkedList<String>();

	/** Number of values to display in the chooser JList. */
	final int LIST_SIZE = 5;

	/** Half of list size. */
	int halfList;

	/** Maximum number of values the user could possibly choose given the
	 increment, and starting and stopping values. */
	int maxValues;

	/** List index we've displayed for the chooser list. */
	int listIndex = 0;

    /**
     * Default constructor.
     */
    public Rev_Dictator() {
        initComponents();
    }

    /**
     *
     */
    public Rev_Dictator (String params, int currentRep) {
        super();
        initComponents();
        this.setVisible (true);
        hideMouse();
        runner.addMouseListener (new Rev_Dictator.CheckMouse());
        this.addMouseListener (new Rev_Dictator.CheckMouse());
		this.getGlassPane().addMouseListener(new Rev_Dictator.CheckMouse());

        secs = GAME_TIME;
        timer.start();
        updateTimerLabel();
        clickLabel.setVisible (false);

		String paramPart[] = params.split(",");
        endowment = Double.parseDouble(paramPart[2]);
        increment = Double.parseDouble (paramPart[3]);
		this.params = params;
		repTot = Integer.parseInt (paramPart[1]);
		repNum = currentRep;
		
        guess = 0;
        thinkTimeRemaining = new Random().nextInt(5) + 3; // [3-7 second wait].

		// Assuming no loss of precision.
		maxValues = (int) (endowment / increment) + 1;
		halfList = (int) Math.floor (LIST_SIZE / 2);
		int numValues = maxValues - halfList + LIST_SIZE;
		for (listIndex = maxValues-halfList;
			listIndex < numValues; listIndex++) {
			moneyValues.add (String.format ("$%.2f",
				increment * (listIndex % maxValues)));
		}
		listIndex--;
		moneyList.setListData(moneyValues.toArray(new String [LIST_SIZE]));
		moneyList.setSelectedIndex(halfList);
		moneyList.addMouseListener (new Rev_Dictator.CheckMouse());
    }

    // GAME --------------------------------------------------------------------
    /**
     * Determines what we should do on left click.
     */
    private void handleLeftClick() {
        if (gameState == GS_GUESS) {
            incrementMoneyList();
        }
        else {
            endGame (EG_NORMAL);
        }
    }
    
    /**
     * Determines what we should do on right click.
     */
    private void handleRightClick() {
        if (gameState == GS_GUESS) {
			String money =
				moneyList.getSelectedValue().toString().substring (1);
			guess = Double.parseDouble (money);

            double percent = getPercentGiven();
            given = percentageToDollars (percent);
            timer.stop();

            setWaitingLayout();
            gameState = GS_WAITING;
            thinkTimer.start();
        }
        else {   
            endGame (EG_NORMAL);
        }
    }

    /**
     * Randomly gets the amount player 1 gives player 2 based on the data file.
     * @return The amount player 1 gave player 2.
     */
    private double getPercentGiven() {
        Random random = new Random();

        try {
            ArrayList<ArrayList<String>> player1Data
                    = readCSVFile ("data/"+DATA_FILE);

            // Turn pdf into cdf.
            double prevValue = 0;
            for (int i=1; i<player1Data.size(); i++) {
                if (i != 1)
                    prevValue = Double.parseDouble(player1Data.get(i-1)
                        .get(CSV_PROBIND));
                double currentValue = Double.parseDouble(player1Data.get(i)
                        .get(CSV_PROBIND));
                double newValue = currentValue + prevValue;
                player1Data.get(i).set(CSV_PROBIND,
                        new Double(newValue).toString());
            }
            if (Double.parseDouble(player1Data.get(player1Data.size()-1)
                    .get(CSV_PROBIND)) != 1) {
                System.err.println ("Error with Rev_Dictator data file: cdf" +
                        " doesn't sum to 1.");
				endGame (EG_BADFILE);
			}

            // Get amount based on cdf.
            double randNum = random.nextFloat();
            int amountIndex = 1;
            while (randNum > Double.parseDouble(player1Data
                    .get(amountIndex).get(CSV_PROBIND))) {
                amountIndex++;
            }

            return Double.parseDouble(player1Data
                    .get(amountIndex).get(CSV_AMOUNTIND));
        }
        catch (Exception e) {
            System.err.println ("Error while trying to process the data file" +
                    " for Dictator-Player2 (Rev_Dictator.java)");
            e.printStackTrace();
			return -1;
        }
    }

    /**
     * Converts a percentage of endowment into dollar amount closest to nearest
     * possible value.
     *
     * @param percent   The percentage of the endowment.
     * @return          The percentage of the endowment in dollars, rounded to
     *                  the nearest increment.
     */
    private double percentageToDollars (double percent) {
        return increment * Math.round (percent * endowment / increment);
    }

    /**
     * Ends the game.
     */
    private void endGame (int status) {
        if (status == EG_NORMAL)
			totalMoneyWon += given;

		String payment = "";
		String oppPay = "";
		String guessStr = "";
		if (status == EG_NORMAL) {
			payment = String.format ("$%.2f", given);
			oppPay = String.format ("$%.2f", (endowment - given));
			guessStr = String.format ("$%.2f", guess);
		}
		else if (status == EG_BADFILE) {
			payment = "BadFile";
			oppPay = "BadFile";
			guessStr = "BadFile";
		}
		else if (status == EG_TIMEOUT) {
			payment = "TimeOut";
			oppPay = "TimeOut";
			guessStr = "TimeOut";
		}
		else {}

		writeLog (playerId + ",2,\"" + params + "\"," + repNum + ","
			+ repTot + "," + payment + "," + oppPay);
		int timeTaken = GAME_TIME - secs;
		writeGameLog (playerId + "," + timeTaken + "," + guessStr);
        try {
            setVisible(false);
            free=true;
        }
        catch(Exception ex) {
            ex.printStackTrace();
            logError(ex);
        }
    }

    // LISTENERS ---------------------------------------------------------------
    /**
     * Defines what we do when the user clicks the mouse.
     */
    class CheckMouse implements MouseListener{
         public void mousePressed (MouseEvent e) { hideMouse(); }
         public void mouseReleased (MouseEvent e) {}
         public void mouseEntered (MouseEvent e) {}
         public void mouseExited (MouseEvent e) {}
        
        /**
         * Handle mouse clicks.
         * 
         * @param e The MouseEvent.
         */
        public void mouseClicked (MouseEvent e){
            int modifiers = e.getModifiers();
            if ((modifiers & InputEvent.BUTTON1_MASK) ==
                    InputEvent.BUTTON1_MASK) {
                handleLeftClick();
            }
            else if ((modifiers & InputEvent.BUTTON3_MASK) ==
                    InputEvent.BUTTON3_MASK) {
                handleRightClick();
            }
            else {}
        }
    }

    /**
     * Handles think timer actions.
     */
    class ThinkTimeListener implements ActionListener {
    	/**
         * Decrement the time remaining, if there is any. If the timer runs out,
         * stop the timer.
         *
         * @param e The ActionEvent.
         */
        
        public void actionPerformed(ActionEvent e) {
            if (thinkTimeRemaining > 0) {
                thinkTimeRemaining--;
            }
            else {
                thinkTimer.stop();
                gameState = GS_RESULT;
                setResultLayout();
            }
    	}
    }

    /**
     * Handles game timer actions.
     */
    class TimeListener implements ActionListener {
    	/**
         * Decrement the time remaining, if there is any. If the user has run
         * out of time, then record the amount of money won and exit.
         *
         * @param e The ActionEvent.
         */
        
        public void actionPerformed(ActionEvent e) {
            if (secs > 0) {
                secs--;
                updateTimerLabel();
            }
            else {
                timer.stop();
                messageLabel.setText ("Sorry, your time is up.");
                endGame (EG_TIMEOUT);
            }
    	}
    }
    
    // LAYOUT ------------------------------------------------------------------
    /**
     * Sets the layout for when player 1 is thinking.
     */
    private void setWaitingLayout() {
        timeLabel.setVisible (false);
        moneyList.setVisible (false);
		moneyScroll.setVisible (false);
        messageLabel.setText ("Waiting for player 1...");
        
        plusMoneyButtonLabel.setVisible (false);
        doneButtonLabel.setVisible (false);
    }

    /**
     * Sets the layout for when we tell the subject how much player 1 actually
     * gave them.
     */
    private void setResultLayout() {
        messageLabel.setText ("Player 1 gave you some amount between $0.00 and "
			+ String.format ("$%.2f", given));
        
        clickLabel.setVisible (true);
    }

    /**
     * Shift the money list to increase by one place.
     */
    private void incrementMoneyList() {
		listIndex++;
		moneyValues.remove();
		moneyValues.add (String.format ("$%.2f",
			increment * (listIndex % maxValues)));
		moneyList.setListData (
			moneyValues.toArray(new String [LIST_SIZE]));
		moneyList.setSelectedIndex (halfList);
    }

    /**
     * Based on the time remaining, updates the time remaining label.
     */
    private void updateTimerLabel() {
        int min = secs / 60;
        int sec = secs % 60;
        timeLabel.setText (min + ":" + ((sec < 10) ? "0" : "") + sec);
    }

    /**
     * Hides the mouse so the user can't see it.
     */
    private void hideMouse() {
        int[] pixels = new int[16 * 16];
        Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
        Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisibleCursor");
        this.getGlassPane().setCursor(transparentCursor);
		this.getGlassPane().setVisible(true);
        runner.setCursor(transparentCursor);
    }
    
    // GENERATED CODE and MAIN -------------------------------------------------
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        timeLabel = new javax.swing.JLabel();
        messageLabel = new javax.swing.JLabel();
        titleLabel = new javax.swing.JLabel();
        plusMoneyButtonLabel = new javax.swing.JLabel();
        doneButtonLabel = new javax.swing.JLabel();
        clickLabel = new javax.swing.JLabel();
        moneyScroll = new javax.swing.JScrollPane();
        moneyList = new javax.swing.JList();

        setBackground(new java.awt.Color(255, 255, 255));
        setBorder(null);
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setMaximumSize(new java.awt.Dimension(800, 600));
        setMinimumSize(new java.awt.Dimension(800, 600));
        setPreferredSize(new java.awt.Dimension(800, 600));

        timeLabel.setFont(new java.awt.Font("Calibri", 1, 36));
        timeLabel.setForeground(new java.awt.Color(153, 102, 0));
        timeLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/hrGlass.png"))); // NOI18N
        timeLabel.setText("3:00");

        messageLabel.setFont(new java.awt.Font("Calibri", 1, 28));
        messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        messageLabel.setText("How much do you think the other player will give you?");

        titleLabel.setFont(new java.awt.Font("Calibri", 1, 28));
        titleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        titleLabel.setText("You are Player 2");

        plusMoneyButtonLabel.setFont(new java.awt.Font("Trebuchet MS", 1, 24));
        plusMoneyButtonLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/redAddMoney.png"))); // NOI18N

        doneButtonLabel.setFont(new java.awt.Font("Trebuchet MS", 1, 24));
        doneButtonLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/greenDone.png"))); // NOI18N

        clickLabel.setFont(new java.awt.Font("Calibri", 1, 28));
        clickLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        clickLabel.setText("Click to end the game.");

        moneyList.setFont(new java.awt.Font("Tahoma", 1, 24));
        moneyList.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "$0.00", "$0.25", "$0.50", "$0.75", "$1.00" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        moneyList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        moneyList.setVisibleRowCount(5);
        moneyList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                moneyListValueChanged(evt);
            }
        });
        moneyScroll.setViewportView(moneyList);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(timeLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE)
                    .add(messageLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE)
                    .add(titleLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE))
                .addContainerGap())
            .add(layout.createSequentialGroup()
                .add(57, 57, 57)
                .add(plusMoneyButtonLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 214, Short.MAX_VALUE)
                .add(doneButtonLabel)
                .add(63, 63, 63))
            .add(layout.createSequentialGroup()
                .add(clickLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE)
                .add(20, 20, 20))
            .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(layout.createSequentialGroup()
                    .add(336, 336, 336)
                    .add(moneyScroll, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 128, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(336, Short.MAX_VALUE)))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .add(timeLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(titleLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(73, 73, 73)
                .add(messageLabel)
                .add(214, 214, 214)
                .add(clickLabel)
                .add(18, 18, 18)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(plusMoneyButtonLabel)
                    .add(doneButtonLabel))
                .add(90, 90, 90))
            .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(layout.createSequentialGroup()
                    .add(220, 220, 220)
                    .add(moneyScroll, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 160, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(256, Short.MAX_VALUE)))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

	/**
	 * Don't let subject change the value of the list.
	 *
	 * @param evt
	 */
	private void moneyListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_moneyListValueChanged
		moneyList.setSelectedIndex (halfList);
	}//GEN-LAST:event_moneyListValueChanged

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        /*try {
            JFrame main = new JFrame();
            JDesktopPane dPane = new JDesktopPane();
            Rev_Dictator frame = new Rev_Dictator();
            main.setSize(820, 620);
            dPane.add(frame);
            main.add(dPane);
            main.setVisible(true);
            dPane.setVisible(true);
            frame.setSelected(true);
            frame.setVisible(true);
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }*/
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel clickLabel;
    private javax.swing.JLabel doneButtonLabel;
    private javax.swing.JLabel messageLabel;
    private javax.swing.JList moneyList;
    private javax.swing.JScrollPane moneyScroll;
    private javax.swing.JLabel plusMoneyButtonLabel;
    private javax.swing.JLabel timeLabel;
    private javax.swing.JLabel titleLabel;
    // End of variables declaration//GEN-END:variables

}
